package jssia.database.mysql.columns;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DecimalColumn extends Column {

    private static final int MAX_DIGITS = 65;
    private static final int MIN_DIGITS = 1;
    private static final int MAX_DECIMAL_DIGITS = 30;

    private final BigInteger maxValue;
    private final int maxDigits;
    private final int numDecimalDigits;

    public DecimalColumn(int maxDigits, int numDecimalDigits) {
        if (maxDigits < MIN_DIGITS || maxDigits > MAX_DIGITS)
            throw new IllegalArgumentException("The maximum number of digits can not be "
                    + maxDigits + ".  It must be between " + MIN_DIGITS + " and " + MAX_DIGITS);
        if (numDecimalDigits < 0 || numDecimalDigits > MAX_DECIMAL_DIGITS)
            throw new IllegalArgumentException("The maximum number of decimal digits can not be "
                    + numDecimalDigits + ".  It must be between 0 and " + MAX_DECIMAL_DIGITS);
        if (numDecimalDigits > maxDigits)
            throw new IllegalArgumentException("The maximum number of decimal digits can not be "
                    + "greater than the max number of digits.  " + numDecimalDigits + " > " + maxDigits);

        this.maxDigits = maxDigits;
        this.numDecimalDigits = numDecimalDigits;
        this.maxValue = BigInteger.valueOf((long) Math.pow(10, maxDigits-numDecimalDigits));
    }

    public int getPrecision() {
        return maxDigits;
    }

    public int getScale() {
        return numDecimalDigits;
    }

    public BigDecimal get(ResultSet rs) throws SQLException {
        return rs.getBigDecimal(fullyQualifiedName);
    }

    public void set(PreparedStatement ps, int index, double value) throws SQLException {
        set(ps, index, new BigDecimal(value));
    }

    public void set(PreparedStatement ps, int index, BigDecimal value) throws SQLException {
        if (value.scale() > numDecimalDigits)
            value = value.setScale(numDecimalDigits, BigDecimal.ROUND_HALF_EVEN);
        else if (value.scale() < 0)
            throw new IllegalArgumentException("MySQL does not support a negative scale");
        if (value.toBigInteger().compareTo(maxValue) > 0)
            throw new IllegalArgumentException("Invalid value of " + value + " , expected the maximum number of digits to be "
                    + maxDigits + " with a precision of " + numDecimalDigits + ".  Therefore the maximum possible value "
                    + " must be less than " + maxValue);

        ps.setBigDecimal(index, value);
    }

    public void update(ResultSet rs, BigDecimal value) throws SQLException {
        if (value.scale() > numDecimalDigits)
            value = value.setScale(numDecimalDigits, BigDecimal.ROUND_HALF_EVEN);
        else if (value.scale() < 0)
            throw new IllegalArgumentException("MySQL does not support a negative scale");
        if (value.toBigInteger().compareTo(maxValue) > 0)
            throw new IllegalArgumentException("Invalid value of " + value + " , expected the maximum number of digits to be "
                    + maxDigits + " with a precision of " + numDecimalDigits + ".  Therefore the maximum possible value "
                    + " must be less than " + maxValue);

        rs.updateBigDecimal(fullyQualifiedName, value);
    }
}
